/*
 * Neutron3529's Unity Game Plugin
 * Copyright (C) 2022 Neutron3529
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * This program is a part of other mod, which could not be used directly
 * all of the bash script would append this file after the main .cs file
 * if you want to compile program manually, you should append it by yourself.
 */

namespace %%NAMESPACE_ID%%
{
    [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false)]
    public class Desc : System.Attribute {
        public string desc;
        public string str;
        public double val;
        public Desc(string desc,string str) {
            this.desc = desc;
            this.str = str;
            this.val = 0;
        }
        public Desc(string desc,double val) {
            this.desc = desc;
            this.str = "";
            this.val = val;
        }
        public Desc(string desc) {
            this.desc = desc;
            this.str = "";
            this.val = 0;
        }
    }
    public partial class Cheat : MelonMod {
        public static HarmonyLib.Harmony harmony;
        // public static BepInEx.Configuration.ConfigFile config;
        public static MelonPreferences_Category config;
#if DEBUG
    #if VERBOSE
        public static Action<string> vlogger;
    #endif
        public static Action<string> logger;
#else
        public static void vlogger(string s){}
        public static void logger(string s){}
#endif
        public static Action<string> logwarn;
        public static void logexception(Exception e){
            logwarn(_strexception(e,0));
        }
        static string _strexception(Exception e, int depth){
            var padding=depth==0?"\n":"\n"+new string(' ',depth*4);
            var content=("\n"+e.GetType().Name+": "+e.Message+"\n"+e.StackTrace).Replace("\n",padding);
            if(e.InnerException !=null){
                return content+_strexception(e.InnerException,depth+1);
            } else {
                return content;
            }
        }
        public abstract class BepInExEntry<T>{
            // public ConfigEntry<T> ce;
            public MelonPreferences_Entry ce;
            public string desc="默认说明";
            public virtual string disable{get{return "默认开关方法说明";}}
            public virtual T _default_disable_val{get{return default(T);}}
            public virtual void Init(ref T confval){
#if VERBOSE
                vlogger("正在修改："+this.GetType().Name);
#endif
                // this.ce=config.Bind("config",this.GetType().Name,confval,this.desc+"（"+this.disable+"）");
                this.ce=config.CreateEntry(this.GetType().Name, confval)
                confval=this.ce.Value;
                if(this.Met(this._default_disable_val))logwarn("警告："+this.GetType()+"("+this.desc+")的_default_disable_val设置失效，这将导致"+this.GetType()+"("+this.desc+"不能正常禁用");
                if(this.Met(confval)){
                    this.Patch();
                    logger(this.desc+"-patched");
                } else {
                    logger(this.desc+" 的数值"+confval+"不满足开启条件：（"+this.disable+"）因而此项修改不生效");
                }
            }
            public virtual void SetDisable(){
                this.ce.Value=this._default_disable_val;
            }
            public virtual void Patch(){
                harmony.PatchAll(this.GetType());
            }
            public virtual bool Met(T confval)=>false;
        }
        public abstract class Bbool : BepInExEntry<bool> {
            public override string disable{get{return "为true时开启，为false时关闭";}}
            public bool val=true;
            public virtual void Init(){
                base.Init(ref this.val);
            }
            public override bool Met(bool confval)=>confval;
        }
        public abstract class Bfloat : BepInExEntry<float> {
//             public static float val;public void Init() {base.Init(ref val);}
            public override string disable{get{return "不为0时开启";}}
            public override bool Met(float confval)=>confval!=0f;
        }
        public abstract class Bsbyte : BepInExEntry<sbyte> {
//             public static int val;public void Init() {base.Init(ref val);} // 应当使用int而非sbyte，sbyte的用途是约束输入数字大小
            public override string disable{get{return "不为0时开启";}}
            public override bool Met(sbyte confval)=>confval!=0;
        }
        public abstract class Bint : BepInExEntry<int> {
//             public static int val;public void Init() {base.Init(ref val);}
            public override string disable{get{return "不为0时开启";}}
            public override bool Met(int confval)=>confval!=0;
        }
        public abstract class Bstring : BepInExEntry<string> {
//             public static string val;public void Init() {base.Init(ref val);}
            public override string _default_disable_val{get{return string.Empty;}}
            public override string disable{get{return "长度大于1时开启";}}
            public override bool Met(string confval)=>confval.Length>1;
        }
        ulong _started=0;
        public override void OnApplicationStart(){this.Start();}
        void Start() {
            if(this._started>0){
                logger("Start方法已经被调用，这是第"+this._started+"次调用，请联系作者修正这个Bug，或者等待作者宣布这是feature");
                return;
            }
            this._started++;
            logwarn=LoggerInstance.Msg;
            LoggerInstance.Msg("此Mod使用AGPL-v3许可发布，如果你使用了这里的代码，请按照相同许可发布你修改后的mod。");
            harmony=new Harmony("%%PLUGIN_ID%%");   // `harmony` is defined in utils.cs
            // config=Config;                          // `utils`   is defined in utils.cs
            config=MelonPreferences.CreateCategory("%%PLUGIN_ID%%");
#if DEBUG
            logger=LoggerInstance.Msg;
            // logger=Log.LogInfo;                  // `logger`  is defined in utils.cs
    #if VERBOSE
            vlogger=LoggerInstance.Msg;              // `vlogger` is defined in utils.cs
    #endif
#endif
            logger("开始注入");
            Type[] t=new Type[0];
            foreach(var type in typeof(Cheat).Module.GetTypes()){
                try{
#if VERBOSE
                    vlogger("搜索到："+type.ToString());
#endif
                    if(type.IsSubclassOf(typeof(Bbool)) || type.IsSubclassOf(typeof(Bfloat)) || type.IsSubclassOf(typeof(Bint)) || type.IsSubclassOf(typeof(Bstring)) || type.IsSubclassOf(typeof(Bsbyte))){
                        object entry=type.GetConstructor(t).Invoke(t);
                        if (entry!=null){
                            Desc desc = (Desc) Attribute.GetCustomAttribute(type, typeof(Desc));
                            if(desc!=null){
                                type.GetField("desc").SetValue(entry,desc.desc);
                                if(type.IsSubclassOf(typeof(Bbool))){
                                    type.GetField("val").SetValue(entry,desc.val>=0);
                                }else if(type.IsSubclassOf(typeof(Bstring))){
                                    type.GetField("val").SetValue(entry,desc.str);
                                }else if(type.IsSubclassOf(typeof(Bfloat))){
                                    type.GetField("val").SetValue(entry,(float)desc.val);
                                }else if(type.IsSubclassOf(typeof(Bsbyte))){
                                    type.GetField("val").SetValue(entry,(sbyte)desc.val);
                                }else{
                                    type.GetField("val").SetValue(entry,(int)desc.val); // for both Bsbyte && Bint
                                }
                            }
                            try {
                                type.GetMethod("Init",t).Invoke(entry,t);
                            } catch (Exception e) {
                                logwarn("Entry："+type+"的Init方法失效，这导致了「"+desc.desc+"」("+type.Name+")不能正常工作。");
                                logexception(e);
                            }
                        }else{logwarn("Entry："+type+"的type.GetConstructor().Invoke()是null，这多半是mod出了问题，如果你看到这个，请联系mod作者。");}
                    }
                } catch (Exception e) {
                    logwarn("Mod出现了意料之外的错误，请及时与Mod作者联系。");
                    logexception(e);
                }
            }
            Start2();
        }
    }
}
